package com.executor.gateway.service.impl;

import com.executor.gateway.mapper.MetricsMapper;
import com.executor.gateway.service.MetricsService;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

import java.lang.reflect.Field;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;

/**
 * 指标服务实现类
 * 支持传统数据库存储和增强的Redis/内存存储两种模式
 * 
 * @author miaoguoxin
 * @date 2024/01/01
 */
@Service
@Slf4j
public class MetricsServiceImpl implements MetricsService {
    
    @Autowired
    private MetricsMapper metricsMapper;

    @Autowired(required = false)
    private ReactiveRedisTemplate<String, String> redisTemplate;

    // 内存计数器，用于高性能计数
    private final Map<String, LongAdder> requestCounters = new ConcurrentHashMap<>();
    private final Map<String, LongAdder> errorCounters = new ConcurrentHashMap<>();
    private final Map<String, AtomicLong> responseTimes = new ConcurrentHashMap<>();

    // Redis键前缀
    private static final String METRICS_KEY_PREFIX = "gateway:metrics:";
    private static final String API_COUNTER_PREFIX = "gateway:api:counter:";
    private static final String SERVICE_STATUS_PREFIX = "gateway:service:status:";
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    private static final DateTimeFormatter HOUR_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH");

    /**
     * 是否启用增强模式
     */
    @Getter
    @Setter
    @Value("${gateway.metrics.enhanced-mode:true}")
    private boolean enhancedMode;

    private ConcurrentHashMap<String, LongAdder> metricsMap = new ConcurrentHashMap<>();

    @Override
    public Mono<Void> incOneCount2MetricsMap(String uri, String countParam) {
        if (enhancedMode) {
            return incrementApiCounter(uri, countParam);
        } else {
            // 传统模式：直接操作内存计数器
            return Mono.fromRunnable(() -> {
                String key = uri + ":" + countParam;
                metricsMap.computeIfAbsent(key, k -> new LongAdder()).increment();
            }).then();
        }
    }

    @Override
    public Mono<Void> save2Db() {
        if (enhancedMode) {
            return persistMetricsToRedis();
        } else {
            // 传统模式：保存到数据库
            return Mono.fromRunnable(() -> {
                try {
                    for (Map.Entry<String, LongAdder> entry : metricsMap.entrySet()) {
                        String key = entry.getKey();
                        long value = entry.getValue().longValue();
                        if (value > 0) {
                            // 保存到数据库的逻辑（需要根据实际情况实现）
                            log.debug("Saving metric to database: {} = {}", key, value);
                        }
                    }
                    metricsMap.clear();
                } catch (Exception e) {
                    log.error("Error saving metrics to database", e);
                }
            }).then();
        }
    }

    @Override
    public Mono<Void> recordRequest(String serviceId, String method, String path, String clientIp, String userAgent, LocalDateTime timestamp) {
        if (!enhancedMode || redisTemplate == null) {
            return Mono.empty();
        }

        String dateStr = timestamp.format(DATE_FORMATTER);
        String hourStr = timestamp.format(HOUR_FORMATTER);
        
        String requestKey = METRICS_KEY_PREFIX + "request:" + serviceId + ":" + dateStr;
        String hourlyKey = METRICS_KEY_PREFIX + "hourly:" + serviceId + ":" + hourStr;
        String methodKey = METRICS_KEY_PREFIX + "method:" + serviceId + ":" + method + ":" + dateStr;

        return redisTemplate.opsForValue().append(requestKey, "1")
                .then(redisTemplate.expire(requestKey, Duration.ofDays(30)))
                .then(redisTemplate.opsForValue().append(hourlyKey, "1"))
                .then(redisTemplate.expire(hourlyKey, Duration.ofDays(7)))
                .then(redisTemplate.opsForValue().append(methodKey, "1"))
                .then(redisTemplate.expire(methodKey, Duration.ofDays(30)))
                .then();
    }

    @Override
    public Mono<Void> recordResponse(String serviceId, int statusCode, long responseTime, LocalDateTime timestamp) {
        if (!enhancedMode || redisTemplate == null) {
            return Mono.empty();
        }

        String dateStr = timestamp.format(DATE_FORMATTER);
        String statusKey = METRICS_KEY_PREFIX + "status:" + serviceId + ":" + statusCode + ":" + dateStr;
        String responseTimeKey = METRICS_KEY_PREFIX + "response_time:" + serviceId + ":" + dateStr;

        return redisTemplate.opsForValue().append(statusKey, "1")
                .then(redisTemplate.expire(statusKey, Duration.ofDays(30)))
                .then(redisTemplate.opsForList().rightPush(responseTimeKey, String.valueOf(responseTime)))
                .then(redisTemplate.expire(responseTimeKey, Duration.ofDays(7)))
                .then();
    }

    @Override
    public Mono<Void> recordError(String serviceId, String errorType, String errorMessage, LocalDateTime timestamp) {
        if (!enhancedMode || redisTemplate == null) {
            return Mono.empty();
        }

        String dateStr = timestamp.format(DATE_FORMATTER);
        String errorKey = METRICS_KEY_PREFIX + "error:" + serviceId + ":" + errorType + ":" + dateStr;
        String errorDetailKey = METRICS_KEY_PREFIX + "error_detail:" + serviceId + ":" + dateStr;

        Map<String, String> errorDetail = new HashMap<>();
        errorDetail.put("type", errorType);
        errorDetail.put("message", errorMessage);
        errorDetail.put("timestamp", timestamp.toString());

        return redisTemplate.opsForValue().append(errorKey, "1")
                .then(redisTemplate.expire(errorKey, Duration.ofDays(30)))
                .then(redisTemplate.opsForList().rightPush(errorDetailKey, errorDetail.toString()))
                .then(redisTemplate.expire(errorDetailKey, Duration.ofDays(7)))
                .then();
    }

    @Override
    public Mono<Void> incrementApiCounter(String apiPath, String counterType) {
        if (!enhancedMode || redisTemplate == null) {
            return Mono.empty();
        }

        String counterKey = API_COUNTER_PREFIX + apiPath + ":" + counterType;
        
        return redisTemplate.opsForValue().append(counterKey, "1")
                .then(redisTemplate.expire(counterKey, Duration.ofDays(30)))
                .then();
    }

    @Override
    public Mono<Void> recordApiResponseTime(String apiPath, long responseTime) {
        if (!enhancedMode || redisTemplate == null) {
            return Mono.empty();
        }

        String responseTimeKey = API_COUNTER_PREFIX + "response_time:" + apiPath;
        
        return redisTemplate.opsForList().rightPush(responseTimeKey, String.valueOf(responseTime))
                .then(redisTemplate.expire(responseTimeKey, Duration.ofDays(7)))
                .then();
    }

    @Override
    public Mono<Void> updateServiceStatus(String serviceId, LocalDateTime timestamp) {
        if (!enhancedMode || redisTemplate == null) {
            return Mono.empty();
        }

        String statusKey = SERVICE_STATUS_PREFIX + serviceId;
        
        Map<String, String> status = new HashMap<>();
        status.put("lastActiveTime", timestamp.toString());
        status.put("status", "active");
        status.put("timestamp", String.valueOf(System.currentTimeMillis()));

        return redisTemplate.opsForHash().putAll(statusKey, status)
                .then(redisTemplate.expire(statusKey, Duration.ofHours(1)))
                .then();
    }

    @Override
    public Mono<Object> getApiStatistics(String apiPath, LocalDateTime startTime, LocalDateTime endTime) {
        if (!enhancedMode || redisTemplate == null) {
            return Mono.just(new HashMap<>());
        }

        String requestKey = API_COUNTER_PREFIX + apiPath + ":request";
        String successKey = API_COUNTER_PREFIX + apiPath + ":success";
        String errorKey = API_COUNTER_PREFIX + apiPath + ":error";
        String responseTimeKey = API_COUNTER_PREFIX + "response_time:" + apiPath;

        return Mono.zip(
                redisTemplate.opsForValue().get(requestKey).defaultIfEmpty("0"),
                redisTemplate.opsForValue().get(successKey).defaultIfEmpty("0"),
                redisTemplate.opsForValue().get(errorKey).defaultIfEmpty("0"),
                redisTemplate.opsForList().size(responseTimeKey).defaultIfEmpty(0L)
        ).map(tuple -> {
            Map<String, Object> statistics = new HashMap<>();
            statistics.put("requestCount", Long.parseLong(tuple.getT1()));
            statistics.put("successCount", Long.parseLong(tuple.getT2()));
            statistics.put("errorCount", Long.parseLong(tuple.getT3()));
            statistics.put("averageResponseTime", calculateAverageResponseTime(responseTimeKey));
            return statistics;
        });
    }

    @Override
    public Mono<Object> getServiceStatistics(String serviceId, LocalDateTime startTime, LocalDateTime endTime) {
        if (!enhancedMode || redisTemplate == null) {
            return Mono.just(new HashMap<>());
        }

        String statusKey = SERVICE_STATUS_PREFIX + serviceId;
        return redisTemplate.opsForHash().entries(statusKey)
                .collectMap(entry -> entry.getKey(), entry -> entry.getValue())
                .cast(Object.class);
    }

    /**
     * 计算平均响应时间
     */
    private double calculateAverageResponseTime(String responseTimeKey) {
        // 这里应该从Redis中获取响应时间列表并计算平均值
        // 为了简化，这里返回一个默认值
        return 0.0;
    }

    /**
     * 持久化指标到Redis
     */
    private Mono<Void> persistMetricsToRedis() {
        if (redisTemplate == null) {
            return Mono.empty();
        }

        // 收集所有需要持久化的操作
        List<Mono<Void>> operations = new ArrayList<>();

        // 持久化请求计数器
        for (Map.Entry<String, LongAdder> entry : requestCounters.entrySet()) {
            String key = METRICS_KEY_PREFIX + "persisted:" + entry.getKey();
            long value = entry.getValue().longValue();
            if (value > 0) {
                Mono<Void> operation = redisTemplate.opsForValue().set(key, String.valueOf(value))
                        .then(redisTemplate.expire(key, Duration.ofDays(30)))
                        .then();
                operations.add(operation);
            }
        }

        // 持久化错误计数器
        for (Map.Entry<String, LongAdder> entry : errorCounters.entrySet()) {
            String key = METRICS_KEY_PREFIX + "persisted:error:" + entry.getKey();
            long value = entry.getValue().longValue();
            if (value > 0) {
                Mono<Void> operation = redisTemplate.opsForValue().set(key, String.valueOf(value))
                        .then(redisTemplate.expire(key, Duration.ofDays(30)))
                        .then();
                operations.add(operation);
            }
        }

        // 并行执行所有操作，然后清理内存计数器
        return Mono.when(operations)
                .doOnSuccess(v -> {
                    requestCounters.clear();
                    errorCounters.clear();
                    responseTimes.clear();
                    log.debug("Metrics persisted to Redis successfully");
                })
                .onErrorResume(e -> {
                    log.error("Error persisting metrics to Redis", e);
                    return Mono.empty();
                });
    }
}